package org.simpleframwork.inject;

import lombok.extern.slf4j.Slf4j;
import org.simpleframwork.core.BeanContainer;
import org.simpleframwork.inject.annotation.Autowired;
import org.simpleframwork.util.ClassUtil;
import org.simpleframwork.util.ValidationUtil;

import java.lang.reflect.Field;
import java.util.Set;

@Slf4j
public class DependencyInjector {

    /**
     * Bean容器
     */
    private BeanContainer beanContainer;

    public DependencyInjector(){
        beanContainer = BeanContainer.getInstance();
    }

    /**
     * 执行IOC
     * @author weiyuewen 2020/6/12 11:28
     */
    public void doIoc(){
        //1 遍历bean容器中的所有Class对象
        if (ValidationUtil.isEmpty(beanContainer.getClasses())){
            log.warn("nothing in container");
            return;
        }
        //2 遍历Class对象的所有成员变量
        for (Class<?> clazz : beanContainer.getClasses()){
            //3 找出被Autowired标记的成员变量
            Field[] fields = clazz.getDeclaredFields();
            if (ValidationUtil.isEmpty(fields)){
                continue;
            }
            for (Field field:fields){
                if (field.isAnnotationPresent(Autowired.class)){
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    String autowiredValue = autowired.value();
                    //4 获取成员变量的类型
                    Class<?> fieldClass = field.getType();
                    //5 获取这些成员变量的类型在容器里对应的实例
                    Object fieldValue = getFieldInstance(fieldClass, autowiredValue);
                    if (fieldValue == null){
                        throw new RuntimeException("unable to inject relevant type, target fieldClass is:" + fieldClass.getName() + " value" + autowiredValue);
                    }else {
                        //6 通过反射将对应成员变量的实例注入到成员变量所在类的实例里
                        Object targetBean = beanContainer.getBean(clazz);
                        ClassUtil.setField(field, targetBean, fieldValue, true);
                    }
                }
            }
        }
   }

   /**
    * 获取字段实例
    * @author weiyuewen 2020/6/12 14:36
    */
    private Object getFieldInstance(Class<?> fieldClass, String autowiredValue) {
        Object fieldValue = beanContainer.getBean(fieldClass);
        if (fieldValue != null){
            return fieldValue;
        }
        Class<?> implementedClass = getImplementClass(fieldClass, autowiredValue);
        if (implementedClass != null){
            return beanContainer.getBean(implementedClass);
        } else {
            return null;
        }
    }

    /**
     * 获取接口实现类
     * @author weiyuewen 2020/6/12 14:36
     */
    private Class<?> getImplementClass(Class<?> fieldClass, String autowiredValue) {
        Set<Class<?>> classSet = beanContainer.getClassesBySuper(fieldClass);
        if (!ValidationUtil.isEmpty(classSet)){
            if (ValidationUtil.isEmpty(autowiredValue)){
                if (classSet.size() == 1){
                    return classSet.iterator().next();
                }
                throw new RuntimeException("multiple implemented classes for " + fieldClass.getName() + " please set @Autowried value to pick one");
            } else {
                for (Class<?> clazz : classSet){
                    if (autowiredValue.equalsIgnoreCase(clazz.getSimpleName())){
                        return clazz;
                    }
                }
            }
        }
        return null;
    }

}
